#include "VxIrrLib.h"

//============================================================================


//============================================================================
//! create entity from spawn data
RCODE EntBase::CreateEntity( const EntSpawnData& oEntSpawnData, irr::f32 f32Scale )
{
	RCODE rc;
	EntTemplate * poEntTemplate = oEntSpawnData.m_poEntTemplate;
	switch( poEntTemplate->m_iModelType )
	{
	case 1:
		// create graphics mesh
		rc = CreateMesh( poEntTemplate, f32Scale );
		break;
	case 2:
		// create graphics billboard
		rc = CreateBillboard( poEntTemplate, f32Scale );
		break;
	default:
		vx_assert( false );
	}
	if( rc )
	{
		return rc;
	}

	btCollisionShape * poPhysShape = CreatePhysicsShape( poEntTemplate, f32Scale );
	vx_assert( poPhysShape );

	// set up physics
	CreatePhysicsBody( oEntSpawnData, poPhysShape );

	// set entity properties from spawn data
	SetEntProperties( oEntSpawnData );
	return rc;
}
//============================================================================
//! create entity helper.. create mesh from template data
RCODE EntBase::CreateMesh( EntTemplate * poEntTemplate, irr::f32 f32Scale )
{
	m_poMesh = m_poIrrBase->m_poSceneMgr->getMesh( GetAssetPathAndName( poEntTemplate->m_strMeshFile.c_str(), eGameAssetTypeMesh ) );
	vx_assert( m_poMesh );
	m_poSceneNode = m_poIrrBase->m_poSceneMgr->addAnimatedMeshSceneNode( m_poMesh );
	vx_assert( m_poSceneNode );
	irr::core::vector3df v3fScale( poEntTemplate->m_f32ModelScale * f32Scale, poEntTemplate->m_f32ModelScale * f32Scale, poEntTemplate->m_f32ModelScale * f32Scale );
	m_poSceneNode->setScale( v3fScale );
	//give children chance override apply textures
	RCODE rc = ApplyTextures( poEntTemplate );
	// add shadow if enabled
	if( UserConfigImp::Instance().m_bShadows ) 
	{
		((irr::scene::IAnimatedMeshSceneNode *)m_poSceneNode)->addShadowVolumeSceneNode();
	}
	if( rc )
	{
		return rc;
	}
	if( m_poMesh )
	{
		return 0;
	}
	return -1;
}
//============================================================================
//! load and apply textures from template
RCODE EntBase::ApplyTextures( EntTemplate * poEntTemplate )
{
	if( poEntTemplate->m_astrTextures[0] != "" )
	{
		m_poSceneNode->setMaterialTexture(0, m_poIrrBase->m_poDriver->getTexture( GetAssetPathAndName( poEntTemplate->m_astrTextures[0].c_str(), eGameAssetTypeTexture ) ) );
		m_poSceneNode->setMaterialFlag(irr::video::EMF_LIGHTING, false);
	}
	return 0;
}
//============================================================================
//! create entity helper.. create billboard from template data
RCODE EntBase::CreateBillboard( EntTemplate * poEntTemplate, irr::f32 f32Scale )
{
	// make the billboard
	irr::scene::ISceneNode* poBillNode = m_poIrrBase->m_poSceneMgr->addBillboardSceneNode(
					NULL, 
					irr::core::dimension2d<irr::f32>(poEntTemplate->m_f32BillboardWidth * f32Scale, poEntTemplate->m_f32BillboardHeight * f32Scale));
	poBillNode->setMaterialFlag( irr::video::EMF_LIGHTING, false );
	poBillNode->setMaterialFlag( irr::video::EMF_ZWRITE_ENABLE, false );
	poBillNode->setMaterialType( irr::video::EMT_TRANSPARENT_ADD_COLOR) ;
	// add the texture
	poBillNode->setMaterialTexture(0, m_poIrrBase->m_poDriver->getTexture( GetAssetPathAndName( poEntTemplate->m_astrTextures[0].c_str(), eGameAssetTypeTexture ) ) );
	// set scale
	poBillNode->setScale( irr::core::vector3df( poEntTemplate->m_f32ModelScale * f32Scale, poEntTemplate->m_f32ModelScale * f32Scale, poEntTemplate->m_f32ModelScale * f32Scale ));
	// shadows dont apply to billboards
	//// add shadow if enabled
	//if( GameConfigImp::Instance().m_bShadows ) 
	//{
	//	((irr::scene::IAnimatedMeshSceneNode *)m_poSceneNode)->addShadowVolumeSceneNode();
	//}
	m_poSceneNode = poBillNode;
	if( poBillNode )
	{
		return 0;
	}
	return -1;
}
//============================================================================
//! create entity helper.. create physics shape.. you should override if not box
btCollisionShape * EntBase::CreatePhysicsShape( EntTemplate * poEntTemplate, irr::f32 f32Scale )
{
	// create physics shape
	btCollisionShape * poCollisionShape = NULL;
	btVector3 vec3HalfExtents = ( poEntTemplate->m_vec3ShapeDimensions * f32Scale) * 0.5f;
	switch( poEntTemplate->m_eShapeType )
	{
	case ePhysGeomTypeInvalid: // invalid or not initialized
	case ePhysGeomTypeBox:
		poCollisionShape = (btCollisionShape *) new btBoxShape( vec3HalfExtents );
		break;
	case ePhysGeomTypeSphere:
		poCollisionShape = (btCollisionShape *) new btSphereShape( poEntTemplate->m_f32Radius * f32Scale );
		break;
	case ePhysGeomTypeCylinder:
		poCollisionShape = (btCollisionShape *) new btCylinderShape( btVector3(  poEntTemplate->m_f32Radius * f32Scale, poEntTemplate->m_vec3ShapeDimensions[1] * f32Scale, poEntTemplate->m_f32Radius * f32Scale ) );
		break;
	case ePhysGeomTypeCapsule:
		//capsule is convex hull of 2 spheres, so use btMultiSphereShape
		//const int numSpheres = 2;
		//F32 height = poEntTemplate->m_vec3ShapeDimensions[1];
		//btVector3 positions[numSpheres] = { btVector3( 0, height, 0 ), btVector3( 0, -height, 0 ) };
		//btScalar radi[numSpheres] = { poEntTemplate->m_f32Radius, poEntTemplate->m_f32Radius };
		//poCollisionShape = (btCollisionShape *) new btMultiSphereShape( positions, radi, numSpheres ) );
		poCollisionShape = (btCollisionShape *) new btCapsuleShape( poEntTemplate->m_f32Radius * f32Scale, poEntTemplate->m_vec3ShapeDimensions[1] * f32Scale );
		break;
	case ePhysGeomTypeCone:
		poCollisionShape = (btCollisionShape *) new btConeShape( poEntTemplate->m_f32Radius * f32Scale, poEntTemplate->m_vec3ShapeDimensions[1] * f32Scale );
		break;
	case ePhysGeomTypeStaticPlane:
		// we only do plane in x-z plane as terrain floor
		//poCollisionShape = (btCollisionShape *) new btConeShape( poEntTemplate->m_f32Radius, m_vec3ShapeDimensions[1] );
		break;
	case ePhysGeomTypeConvexMesh:
		poCollisionShape = (btCollisionShape *) new btConvexHullShape();
		break;
	case ePhysGeomTypeConcaveMesh:
		//poCollisionShape = (btCollisionShape *) new btConcaveShape();
		break;
	case ePhysGeomTypeBBoxFromNode:
		if( this->m_poSceneNode )
		{
			// get bounding box 


		}
		//poCollisionShape = (btCollisionShape *) new btBoxShape( vec3HalfExtents );
		break;
	}
	vx_assert( poCollisionShape );
	return poCollisionShape;
}
//============================================================================
//! create entity helper.. create a physics body object and add it to the world
void EntBase::CreatePhysicsBody( const EntSpawnData& oEntSpawnData, btCollisionShape * poPhysShape )
{
	vx_assert( poPhysShape );
	vx_assert( m_poSceneNode );
	EntTemplate * poEntTemplate = oEntSpawnData.m_poEntTemplate;
	m_s32TemplateId = poEntTemplate->GetTemplateId();

	//// Rotation
	//btQuaternion oQuaternionRotation(	oEntSpawnData.m_vec3Rotation[1] * irr::core::DEGTORAD, 
	//									oEntSpawnData.m_vec3Rotation[0] * irr::core::DEGTORAD, 
	//									oEntSpawnData.m_vec3Rotation[2] * irr::core::DEGTORAD );
	//// Transform
	//btTransform oTransform;
	//oTransform.setIdentity();
	//oTransform.setOrigin( btVector3( oEntSpawnData.m_vec3Position[0], oEntSpawnData.m_vec3Position[1], oEntSpawnData.m_vec3Position[2]) );
	//oTransform.setRotation( oQuaternionRotation );

	// Create motion state
	//btDefaultMotionState * poMotionState = new btDefaultMotionState( oTransform );
	m_poMotionState = CreateMotionState( m_poSceneNode );
	vx_assert( m_poMotionState );
	

	// Local inertia
	btVector3 LocalInertia(0.0f, 0.0f, 0.0f);
	//if( poEntTemplate->m_f32Mass != 0.0f)
	{
		poPhysShape->calculateLocalInertia( poEntTemplate->m_f32Mass, LocalInertia );
	}

	// create physics body
	m_poRigidBody = new btRigidBody( poEntTemplate->m_f32Mass, m_poMotionState, poPhysShape, LocalInertia);
	vx_assert( m_poRigidBody );
	m_poRigidBody->setUserPointer( (void *)this );
	m_poRigidBody->setFriction( poEntTemplate->m_f32Friction) ;
	m_poRigidBody->setDamping( poEntTemplate->m_f32LinearDamping, poEntTemplate->m_f32AngularDamping );
	m_poRigidBody->setSleepingThresholds(0.2f, 0.2f);

	// add physics body to world
	m_poIrrBase->m_poPhysBase->GetPhysWorld()->addRigidBody( m_poRigidBody, poEntTemplate->m_eCollisionGroup, poEntTemplate->m_eCollisionMask);

}

//============================================================================
//! sets entity properties from spawn data
void EntBase::SetEntProperties(const EntSpawnData& oEntSpawnData ) 
{
	// set ident properties
	m_strEntProperName = oEntSpawnData.m_strEntProperName;

	EntTemplate * poEntTemplate = oEntSpawnData.m_poEntTemplate;
	m_strEntCommonName = poEntTemplate->GetCommonName();
	m_eEntType = poEntTemplate->m_eEntType;
	m_s32LifeTimeMs = poEntTemplate->m_s32LifeTimeMs;

	// set graphics properties
	if( m_poSceneNode ) 
	{
		SetIrrPosition( irr::core::vector3df( oEntSpawnData.m_vec3Position[0], oEntSpawnData.m_vec3Position[1], oEntSpawnData.m_vec3Position[2] ) );
		SetIrrRotation( irr::core::vector3df( oEntSpawnData.m_vec3Rotation[0], oEntSpawnData.m_vec3Rotation[1], oEntSpawnData.m_vec3Rotation[2] ) );
		m_poSceneNode->setMaterialFlag(irr::video::EMF_NORMALIZE_NORMALS, true);
		m_poSceneNode->setMaterialFlag(irr::video::EMF_TRILINEAR_FILTER, UserConfigImp::Instance().m_bTrilinearFiltering );
		m_poSceneNode->getMaterial(0).TextureLayer[0].AnisotropicFilter = UserConfigImp::Instance().m_iAnisotropicFiltering;
	}

	// set physics properties
	if( m_poRigidBody ) 
	{
		SetPhysicsPosition( oEntSpawnData.m_vec3Position );
		SetPhysicsRotation( irr::core::vector3df( oEntSpawnData.m_vec3Rotation[0], oEntSpawnData.m_vec3Rotation[1], oEntSpawnData.m_vec3Rotation[2] ));
		m_poRigidBody->setFriction( poEntTemplate->m_f32Friction );
		m_poRigidBody->setDamping( poEntTemplate->m_f32LinearDamping, poEntTemplate->m_f32AngularDamping);
		m_vec3LastPosition = GetPhysicsPosition();
	}

	// Collision
	m_strScriptCollisionCallback = poEntTemplate->m_strScriptCollisionCallback;
}

//============================================================================
//! set entity constraint properties
void EntBase::SetEntProperties(const EntConstraintData * poEntConstraintData ) 
{
	EntTemplate * poEntTemplate = poEntConstraintData->m_poEntTemplate;

	// Basic properties
	m_strEntProperName	= poEntConstraintData->m_strConstraintName;
	m_strEntCommonName	= poEntTemplate->m_strEntCommonName;
	m_eEntType			= poEntTemplate->m_eEntType;
	m_s32LifeTimeMs		= poEntTemplate->m_s32LifeTimeMs;
}

//============================================================================
//! create Motion State.. give derived classes a chance to override
IrrPhysMotionState * EntBase::CreateMotionState( irr::scene::ISceneNode * poSceneNode )
{
	log_msg( 0, "EntBase::CreateMotionState: node %d\n", poSceneNode );
	return new IrrPhysMotionState( poSceneNode );
}


